/*
	*************************************************************************************

	Logger.h

	Author:		Matthias Gudmundsson
	Created:	June 1998
	Revised:	April 1999
	OS:			Win32
	Project:	Utilities

	Description:

		Logging utility - client side.

	(c) OZ.COM 1998, 1999




		Initialization
		__________________________________

		There is one macro to initialize channels:

		LOGOBJECT(facility, objectname, functionname)

		Put this somewhere in your .cpp file outside a function block.

		In WinMain or DllMain put this:

		LOGINITIALIZE(hInstance, "OV3");

		This will identify your .dll or .exe and start the log library
		associated with the log device named "OV3".  You pulsu-people
		will put "iPulse" instead of "OV3".

		It's also nice to put LOGTERMINATE() in WinMain or DllMain to
		shut down and clean up resources and stuff.



		Logging
		__________________________________

		There is one macro used to log stuff:

		LOGXXX(flag, string, v1, ..., vn)


		This macro stuffs the sprintf'd string into the logserver.
		The XXX is a name defined by the user in LOGOBJECT (see above).
		'flag' is LGINFO, LGWARN, LGERR, LGFATAL or LGOVERLAP.  These flags
		are interpreted by the log server and simply indicate which
		icon to put in front of the log message (generally speaking).



		Extra features
		__________________________________

		The function LogIsLogging() returns 1 if there is a log server
		running, 0 otherwise.

		By defining DISABLELOGGER, no traces of any log code will remain after
		compilation.

		Channel can also be declared using LOGOBJECTD.  Those channels will
		evaluate to nothing in release builds.  Please use the 'D' versions
		only for totally trivial logs.  Example:

			LOGOBJECT("fac", "obj", EX1);
			LOGOBJECTD("fac", "obj", EX2);

		Both LOGEX1 and LOGEX2 log to the same channel, but LOGEX2 will
		disappear in release builds.




		Sample of usage:
		__________________________________


		Scenario:  The 3D Viewer plugin

		Somewhere at the top of Vw3dPlugin.cpp we put this:

		LOGOBJECT("OPIUM Plugin", "3DViewer stats", STAT)
		LOGOBJECT("OPIUM Plugin", "3DViewer", 3D)
		LOGOBJECT("OBS Port", "3DViewer", PORT)
		LOGOBJECTD("OBS Port", "3DViewer", PORTD)



		.. now we have three log functions: LOGSTAT, LOG3D and LOGPORT
		and somewhere in the code we call these functions:


		LOGSTAT(LGOVERLAP, "Framerate: %.02f", fps);

		...

		LOGSTAT(LGINFO, "Initializing 3d stuff:");
		LOGSTAT(LGINFO, "...Number of polygons: %d", numpolys);
		LOGSTAT(LGINFO, "...Number of triangles: %d", numtriangles);
		LOGSTAT(LGINFO, "...Number of the beast: %d", 666);

		...

		LOG3D(LGINFO, "Just recreated the 3d window");

		...

		LOG3D(LGWARNING, "Couldn't do funky stuff.  Errorcode: %d", err);

		...

		LOG3D(LGFATAL, "The hardware failed.  Reason: %s", reason);

		...

		LOGPORT(LGINFO, "Communication link established");
		LOGPORT(LGINFO, "The port just poked the plugin!");
		LOGPORTD(LGINFO, "This message will only show up in debug builds");


*/



#ifndef __cplusplus
#error This wunderstuff requires C++ compilation, sorry :(
#endif

#ifndef __LOGGER_H__
#define __LOGGER_H__
#ifdef WIN32

#include <stdarg.h>
#include <stdio.h>

#pragma pack(push, logger, 8)

typedef short TLOGCHANNELID;
typedef long TLOGSOURCEID;

typedef struct TLOGOBJECT
{
	long oktocall;
	const char *facility;
	const char *object;
	TLOGCHANNELID		channel;			// index into channel buffer
	TLOGSOURCEID		source;				// dynamic unique id for this instance
} TLOGOBJECT;



typedef enum TLOGFLAG
{

	LGINFO =		0x01,
	LGWARN =		0x02,
	LGERR =			0x04,
	LGFATAL =		0x08,

	LGOVERLAP =		0x10,
	LGPERF =		0x20,
	LGCOUNT =		0x40,

        /* multi line message flags - duplicate above,
         * but with MLOG_FLAG set.
         */

	MLGINFO =		0x101,
	MLGWARN =		0x102,
	MLGERR =		0x104,
	MLGFATAL =		0x108,

	MLGOVERLAP =	        0x110,
	MLGPERF =		0x120,
	MLGCOUNT =		0x140,

	MLOG_FLAG =             0x100,   // yes, this is cheating.

} TLOGFLAG;

#if CCP_LOGGER_EMBEDDED
	#define LOGIMPORT
#else
	#define LOGIMPORT BLUEIMPORT
#endif

namespace CCP
{
	LOGIMPORT void LogToLogObject_v( TLOGOBJECT& logobj, TLOGFLAG flag, const char* format, va_list args );
}

extern LOGIMPORT bool g_isLoggingEnabled;

int Log__InitLibrary(
	LONG_PTR module,
	const char *logdevname
	);

void Log__TermLibrary(
	);

void Log__global(
	TLOGOBJECT &logobj,
	TLOGFLAG flag,
	const char *format,
	va_list argptr
	);



// hmm......

#ifdef COMPILING_LOGGERLIB
extern inline int log_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr );
#else
inline int log_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr )
{
#ifndef _ATL_MIN_CRT
	//extern int _vsnprintf(char *buffer, size_t count, const char *format, va_list argptr );
	return vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
#else
	return wvsprintf(buffer, format, argptr);
#endif
}
#endif

#ifndef DISABLELOGGER

bool Log__IsLogging();

inline long LogIsLogging()
{
	return Log__IsLogging();
}

#if CCP_LOGGER_EMBEDDED
#define LOGINITIALIZE(inst, name) Log__InitLibrary((LONG_PTR)inst, name)
#define LOGTERMINATE() Log__TermLibrary()
#else
#define LOGINITIALIZE(inst, name)
#define LOGTERMINATE()
#endif

#define LOGOBJECT(fac, obj, mname) \
	static void LOG##mname(	\
		TLOGFLAG flag,\
		const char *format, \
		...\
		)\
	{\
		if (!g_isLoggingEnabled)\
			return;\
		static TLOGOBJECT logobj = {1, fac, obj, 0}; \
		if (!logobj.oktocall) \
			return;\
		va_list arglist; \
		va_start(arglist, format); \
		CCP::LogToLogObject_v( logobj, flag, format, arglist ); \
		va_end(arglist); \
	}

#ifdef _DEBUG

#define LOGOBJECTD(fac, obj, mname) \
	static void LOG##mname(	\
		TLOGFLAG flag,\
		const char *format, \
		...\
		)\
	{\
	  static TLOGOBJECT logobj = {1, fac, obj, 0}; \
		if (!logobj.oktocall) \
			return; \
		va_list arglist; \
		va_start(arglist, format); \
		CCP::LogToLogObject_v( logobj, flag, format, arglist ); \
		va_end(arglist); \
	}

#else // _DEBUG

#define LOGOBJECTD(fac, obj, mname) \
	static void LOG##mname(	\
		TLOGFLAG flag,\
		const char *format, \
		...\
		)\
	{\
	}

#endif // _DEBUG


#else // DISABLELOGGER

#define LOGINITIALIZE(inst, name)
#define LOGTERMINATE()

#define LOGOBJECT(fac, obj, mname) \
	static void LOG##mname(	\
		TLOGFLAG flag,\
		const char *format, \
		...\
		)\
	{\
	}

#define LOGOBJECTD(fac, obj, mname) \
	static void LOG##mname(	\
		TLOGFLAG flag,\
		const char *format, \
		...\
		)\
	{\
	}

inline long LogIsLogging()
{
	return false;
}

#endif // DISABLELOGGER
#pragma pack(pop, logger)
#endif //WIN32
#endif // __LOGGER_H__

